using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Content;

using Fusion;

namespace Fusion
{
    /// <summary> leet hax </summary>
    public class Hax
    {


        #region Randomization

        // used for local random functions
        private static Random pRnd;
        private static Random rnd
        {
            get
            {
                if( pRnd == null )
                    pRnd = new Random();
                return pRnd;
            }
        }
		public static bool RandomBool()
		{
			return D6() >= 4;
		}
        public static float Random()
        {
            return ( float )Hax.rnd.NextDouble();
        }
        public static float ChaosTwister() // For when Random() just isn't random enough: 
        {
			int steps = ( (int)Hax.rnd.NextDouble() % (int)Hax.rnd.NextDouble() );
            double value = Hax.rnd.NextDouble();

            for( int i=0; i < steps; i++ )
            {
                // Seed it often
                value = ( value + Hax.rnd.NextDouble() ) / 2;

                // Twist it sometimes
				if( Hax.RandomBool() == true )
                    value = ( ~(ulong)( value * 10000000000000000 ) ) / 10000000000000000;
            }

            return (float)value;
        }
        public static float Random( float min, float max )
        {
            return min + ( Random() * ( max - min ) );
        }
        public static int RndmSign()
        {
            double d = 0.5;
            while( d == 0.5 )
                d = Hax.rnd.NextDouble();

            if( Hax.rnd.NextDouble() > 0.5 )
                return 1;
            else
                return -1;
        }

        public static int D6()
        {
            return 1 + Hax.rnd.Next( 5 );
        }
        public static int D6( int n )
        {
            int ret = 0;
            while( n > 0 )
            {
                ret += 1 + Hax.rnd.Next( 5 );
                n--;
            }
            return ret;
        }

        #endregion

        #region Clamp

        public static Vector2 ClampLength( Vector2 value, float max )
        {
            if( value.Length() > max )
                return Vector2.Normalize( value ) * max;
            else
                return value;
        }
        public static Vector3 ClampLength( Vector3 value, float max )
        {
            if( value.Length() > max )
                return Vector3.Normalize( value ) * max;
            else
                return value;
        }
        public static float Clamp( float angle )
        {
            while( angle < 0 )
                angle += MathHelper.TwoPi;
            while( angle > MathHelper.TwoPi )
                angle -= MathHelper.TwoPi;

            return angle;
        }
        public static int Clamp( int value, int max )
        {
            if( value > max )
                return max;
            else if( value < -max )
                return -max;
            else
                return value;
        }
        public static int Clamp( int value, int min, int max )
        {
            if( value > max )
                return max;
            else if( value < min )
                return min;
            else
                return value;
        }
        public static float Clamp( float value, float max )
        {
            if( value > max )
                return max;
            else if( value < -max )
                return -max;
            else
                return value;
        }
        public static float Clamp( float value, float min, float max )
        {
            if( value > max )
                return max;
            else if( value < min )
                return min;
            else
                return value;
        }
        public static Vector2 Clamp( Vector2 value, float max )
        {
            return Vector2.Clamp( value, Vector2.One * -max, Vector2.One * max );
        }
        public static Vector2 Clamp( Vector2 value, float min, float max )
        {
            return Vector2.Clamp( value, Vector2.One * min, Vector2.One * max );
        }
        public static Vector3 Clamp( Vector3 value, float max )
        {
            return Vector3.Clamp( value, Vector3.One * -max, Vector3.One * max );
        }
        public static Vector3 Clamp( Vector3 value, float min, float max )
        {
            return Vector3.Clamp( value, Vector3.One * min, Vector3.One * max );
        }
        #endregion

        #region Step
        // Step functions move from one value to another, by a certain amount - and stop if they move over the target amount

        public static float Step( float from, float to, float amt )
        {
            if( from < to )
            {
                from += amt;
                if( from > to )
                    return to;
            }
            else if( from > to )
            {
                from -= amt;
                if( from < to )
                    return to;
            }
            return from;
        }

        public static Vector2 Step( Vector2 from, Vector2 to, float amt )
        {
            return new Vector2( Step( from.X, to.X, amt )
                              , Step( from.Y, to.Y, amt ) );
        }
        public static Vector2 Step( Vector2 from, Vector2 to, Vector2 amt )
        {
            return new Vector2( Step( from.X, to.X, amt.X )
                              , Step( from.Y, to.Y, amt.Y ) );
        }

        public static Vector3 Step( Vector3 from, Vector3 to, float amt )
        {
            return new Vector3( Step( from.X, to.X, amt )
                              , Step( from.Y, to.Y, amt )
                              , Step( from.Z, to.Z, amt ) );
        }
        public static Vector3 Step( Vector3 from, Vector3 to, Vector3 amt )
        {
            return new Vector3( Step( from.X, to.X, amt.X )
                              , Step( from.Y, to.Y, amt.Y )
                              , Step( from.Z, to.Z, amt.Z ) );
        }

        public static Vector4 Step( Vector4 from, Vector4 to, float amt )
        {
            return new Vector4( Step( from.X, to.X, amt )
                              , Step( from.Y, to.Y, amt )
                              , Step( from.Z, to.Z, amt )
                              , Step( from.W, to.W, amt ) );
        }
        public static Vector4 Step( Vector4 from, Vector4 to, Vector4 amt )
        {
            return new Vector4( Step( from.X, to.X, amt.X )
                              , Step( from.Y, to.Y, amt.Y )
                              , Step( from.Z, to.Z, amt.Z )
                              , Step( from.W, to.W, amt.W ) );
        }

        public static Color Step( Color from, Color to, float amt )
        { return new Color( Step( from.ToVector4(), to.ToVector4(), amt ) ); }
        public static Color Step( Color from, Color to, Color amt )
        { return new Color( Step( from.ToVector4(), to.ToVector4(), amt.ToVector4() ) ); }
        #endregion

        #region Angle

        // calculates wether an angle is closer by going negative or positive
        public static int AngleShort( float aFrom, float aTo )
        {
            aFrom = Clamp( aFrom );
            aTo = Clamp( aTo );

            if( aTo > aFrom )
            {
                if( aTo - MathHelper.Pi <= aFrom ) return 1;
                else return -1;
            }

            if( aFrom > aTo )
            {
                if( aFrom - MathHelper.Pi <= aTo ) return -1;
                else return 1;
            }

            return 0;

        }

        public static float AngleDistance( float a, float b )
        {
            float low;
            float high;

            if( a > b ) { high = a; low = b; }
            else { high = b; low = a; }

            if( high - low > Math.PI )
                return ( ( (float)Math.PI * 2 ) - high ) + low;
            else return high - low;
        }

        // returns angle of a vector
        public static float ToAngle( float x, float y )
        {
            Vector2 v = new Vector2( x, y );
            if( v.Length() == 0 )
                return 0.0f;

            v.Normalize();
            return ( float )Math.Atan2( v.Y, v.X ) + MathHelper.PiOver2;
        }


		public static float AngleStep( float from, float to, float amt )
		{

			float dir = AngleShort( from, to );
			float target = from + ( amt * dir );

			if( dir == 1 )
			{
				if( to < from )
				{
					if( target > MathHelper.TwoPi * 2 )
						return Math.Min( target - MathHelper.TwoPi, to );
					else
						return target;
				}
				else
					return Math.Min( target, to );
			}
			else if( dir == -1 )
			{
				if( to > from )
				{
					if( target < 0 )
						return Math.Max( target + MathHelper.TwoPi, to );
					else
						return target;
				}
				else
					return Math.Max( target, to );
			}
			else
				return from;
		}

        #endregion


        #region Vector
        


        // Minimum
        public static Vector2 Min( Vector2 one, Vector2 two )
        {
            return new Vector2( MathHelper.Min( one.X, two.X ), MathHelper.Min( one.Y, two.Y ) );
        }
        public static Vector2 Mid( Vector2 one, Vector2 two )
        {
            Vector2 tl = Min( one, two );
            Vector2 br = Max( one, two );
            return tl + ((br - tl) * 0.5f);
        }
        public static Vector2 Max( Vector2 one, Vector2 two )
        {
            return new Vector2( MathHelper.Max( one.X, two.X ), MathHelper.Max( one.Y, two.Y ) );
        }


        public static Vector3 Min( Vector3 one, Vector3 two )
        {
            return new Vector3( MathHelper.Min( one.X, two.X ), MathHelper.Min( one.Y, two.Y ), MathHelper.Min( one.Z, two.Z ) );
        }
        public static Vector3 Mid( Vector3 one, Vector3 two )
        {
            Vector3 tl = Min( one, two );
            Vector3 br = Max( one, two );
            return tl + ( ( br - tl ) * 0.5f );
        }
        public static Vector3 Max( Vector3 one, Vector3 two )
        {
            return new Vector3( MathHelper.Max( one.X, two.X ), MathHelper.Max( one.Y, two.Y ), MathHelper.Max( one.Z, two.Z ) );
        }


        // Look

        // Linear Interpolation
        public static Vector2 Lerp( Vector2 from, Vector2 to, float scale )
        {
            Vector2 dif = to - from;
            return from + ( dif * scale );
        }
        public static Vector3 Lerp( Vector3 from, Vector3 to, float scale )
        {
            Vector3 dif = to - from;
            return from + (dif * scale);
        }
        #endregion

        #region Collision

        // Returns 2 times the signed triangle area. The result is positive if
        // abc is ccw, negative if abc is cw, zero if abc is degenerate.
        public static float TriArea( Vector2 a, Vector2 b, Vector2 c )
        {
            return ( a.X - c.X ) * ( b.Y - c.Y ) - ( a.Y - c.Y ) * ( b.X - c.X );
        }

        /// <summary> Tests if a point is inside a rectangular area </summary>
        /// <param name="point">The point to test</param>
        /// <param name="center">The center of the area</param>
        /// <param name="size">The size of the area</param>
        /// <returns>True if point is inside the specified area</returns>
        public static bool Overlaps( Vector2 point, Vector2 center, Vector2 size )
        {
            if( point.X >= center.X - (size.X / 2) && point.X <= center.X + (size.X / 2)
             && point.Y >= center.Y - (size.Y / 2) && point.Y <= center.Y + (size.Y / 2) )
                return true;
            else
                return false;
        }

        /// <summary> Test if a point is inside a shape </summary>
        public static bool Overlaps( Vector2 point, Vector2[] shape )
        {
            int numIntersections = 0;

            for( int j = 0; j < shape.Length - 1; j++ )
            {
                float t = 0;
                Vector2 p = Vector2.Zero;
                if( Line.Intersects( point, point + Vector2.One * 100000.0f, shape[j], shape[j + 1], ref t, ref p ) )
                    numIntersections++;
            }

            return ( ( numIntersections % 2 ) != 0 );
        }

		/// <summary> Tests intersection between a point, and an axis-aligned boxes </summary>
		/// <param name="point">The point to test</param>
		/// <param name="center">The center of the box to test</param>
		/// <param name="size">The size of the box to test</param>
		/// <param name="projection">The shortest distance which box A is projected into box B</param>
		/// <returns>True if there is intersection, else false</returns>
		public static bool Intersect( Vector2 point, Vector2 tl, Vector2 size, ref Vector2 projection )
		{
			projection = Vector2.Zero;
			Vector2 translation = (tl + (size/2)).TranslationTo( point );
			Vector2 sumWidth = size / 2;

			// Find X projection
			if( translation.X < 0 )
			{
				if( sumWidth.X > -translation.X )
					projection.X = -sumWidth.X - translation.X;
				else
					return false;
			}
			else if( translation.X > 0 )
			{
				if( sumWidth.X > translation.X )
					projection.X = sumWidth.X - translation.X;
				else
					return false;
			}

			// Find Y Projection
			if( translation.Y < 0 )
			{
				if( sumWidth.Y > -translation.Y )
					projection.Y = -sumWidth.Y - translation.Y;
				else
					return false;
			}
			else if( translation.Y > 0 )
			{
				if( sumWidth.Y > translation.Y )
					projection.Y = sumWidth.Y - translation.Y;
				else
					return false;
			}

			// Shortest projection axis only plz
			Vector2 pn = projection.Unsigned();
			if( pn.X > pn.Y ) projection.X = 0;
			else projection.Y = 0;

			return true;
		} // Point vs AABox

		/// <summary> Tests intersection between two axis-aligned boxes </summary>
		/// <param name="aCenter">The center of box A</param>
		/// <param name="aSize">The size of box A</param>
		/// <param name="bCenter">The center of box B</param>
		/// <param name="bSize">The size of box B</param>
		/// <param name="projection">The shortest distance which box A is projected into box B</param>
		/// <returns>True if there is intersection, else false</returns>
		public static bool Intersect( Vector2 aPos, Vector2 aSize, Vector2 bPos, Vector2 bSize, ref Vector2 projection )
		{
			projection = Vector2.Zero;
			Vector2 translation = (bPos + (bSize/2)).TranslationTo( aPos + (aSize/2));
			Vector2 sumWidth = ( aSize / 2 ) + ( bSize / 2 );

			// Find X projection
			if( translation.X < 0 )
			{
				if( sumWidth.X >= -translation.X )
					projection.X = -sumWidth.X - translation.X;
				else
					return false;
			}
			else if( translation.X > 0 )
			{
				if( sumWidth.X >= translation.X )
					projection.X = sumWidth.X - translation.X;
				else
					return false;
			}

			// Find Y Projection
			if( translation.Y < 0 )
			{
				if( sumWidth.Y >= -translation.Y )
					projection.Y = -sumWidth.Y - translation.Y;
				else
					return false;
			}
			else if( translation.Y > 0 )
			{
				if( sumWidth.Y >= translation.Y )
					projection.Y = sumWidth.Y - translation.Y;
				else
					return false;
			}

			// Shortest projection axis only plz
			Vector2 pn = projection.Unsigned();
			if( pn.X > pn.Y )
				projection.X = 0;
			else
				projection.Y = 0;

			return true;
		} // AABox vs AABox

        #endregion

        #region Useful Functions

        /// <summary>
        /// Returns a floating point period from a GameTime, used as a multiplier
        /// </summary>
        /// <param name="time">GameTime to retrieve the period from</param>
        /// <returns></returns>
        public static float Period( GameTime time )
        {
            return ( float )time.ElapsedGameTime.TotalSeconds;
        }

        /// <summary>
        /// Bool timer; increases value if true, decreases if false
        /// </summary>
        /// <param name="state"></param>
        /// <param name="value"></param>
        /// <param name="max"></param>
        /// <returns></returns>
        public static void BoolTimer( bool state, ref int value, int max )
        {
            if( state == true )
            {
                if( value < 0 )
                    value = 0;
                if( value < max )
                    value++;
            }
            else
            {
                if( value > 0 )
                    value = 0;
                if( value > -max )
                    value--;
            }
        }
        /// <summary>
        /// random float from 0.0f to 1.0f
        /// </summary>
        /// <returns></returns>
        /// <summary>
        /// random float from min to max
        /// </summary>
        /// <returns></returns>
        /// <summary>
        /// randomised sign
        /// </summary>
        /// <returns>either 1 or -1</returns>
        
        // rounder!
        public static float Round( float fValue, float fIncrement )
        {
            float fMult = 10000000.0f;
            fValue *= fMult;
            fIncrement *= fMult;

            if( fValue % fIncrement >= fIncrement * 0.5f )
                return ( ( fValue - ( fValue % fIncrement ) ) + fIncrement ) / fMult;
            else
                return ( fValue - ( fValue % fIncrement ) ) / fMult;
        }

        public static Color Alpha( Color c, float a )
        {
            return new Color( new Vector4( c.R / 255.0f, c.G / 255.0f, c.B / 255.0f, a ) );
        }

        #endregion // Useful functions
    }
}
